#include "simgraphicdata.h"

#include "../../editors/items/graphicitemcommon.h"
#include "../../editors/items/isimitem.h"
#include "../../mainwindow.h"
#include "../../undo/simulationcommands.h"
#include "../../utils/undomanager.h"
#include "../../utils/workflownetgenerator.h"

#include <ieditoritem.h>
#include <wfnet.h>

#include <QtGui/QGraphicsSceneMouseEvent>
#include <QtGui/QMessageBox>

using namespace core;
using namespace swp;
using namespace wfnet;

SimGraphicData::SimGraphicData() : GraphicData(),
    m_simPath(),
    m_oldSimPath(),
    m_simPathEdit(false),
    m_net(0),
    m_isNetDirty(true)
{

}

SimGraphicData::~SimGraphicData()
{

}

QList<QUuid> SimGraphicData::simPath() const
{
    return m_simPath;
}

void SimGraphicData::tryToCreateSimPath(const QList<QUuid>& simPath)
{
    foreach (const QUuid& id, simPath)
    {
        if (m_idToInstanceMapping.contains(id))
        {
            IEditorItem* editorItem = m_idToInstanceMapping[id];

            ISimItem* simItem = dynamic_cast<ISimItem* >(editorItem);
            IGraphicEditorItem* gEditorItem =
                dynamic_cast<IGraphicEditorItem* >(editorItem);

            Q_ASSERT(simItem != 0 && gEditorItem != 0); // can't happen

            tryToAddToSimPath(simItem, gEditorItem);
        }
        else
        {
            break;
        }
    }

    m_oldSimPath = m_simPath;
}

void SimGraphicData::setCurrentState(IEditor::State state)
{
    GraphicData::setCurrentState(state);

    if (m_state == IEditor::Connect)
    {
        m_simPathEdit = true;
    }
    else if (m_simPathEdit)
    {
        checkSimPath();

        m_simPathEdit = false;
    }
}

void SimGraphicData::clearData(const QList<core::IEditorItem* >& preserve)
{
    GraphicData::clearData(preserve);

    m_simPath.clear();
}

bool SimGraphicData::isNetDirty() const
{
    return m_isNetDirty;
}

WfNet* SimGraphicData::net() const
{
    return m_net;
}

void SimGraphicData::updateNet()
{
    if (m_net != 0)
    {
        WorkflowNetGenerator::getInstance()->destroyNet(m_net);
    }

    m_net = WorkflowNetGenerator::getInstance()->generate(this);

    m_isNetDirty = false;
}

void SimGraphicData::mousePressEvent(QGraphicsSceneMouseEvent* event)
{
    QGraphicsScene::mousePressEvent(event);
}

void SimGraphicData::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
{
    QGraphicsScene::mouseMoveEvent(event);
}

void SimGraphicData::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
{
    if (m_state == IEditor::Connect)
    {
        const qreal x = event->scenePos().x();
        const qreal y = event->scenePos().y();

        QGraphicsItem* item = itemAt(x, y);
        if (item != 0)
        {
            if (item->parentItem() != 0)
            {
                while (item->parentItem() != 0)
                {
                    item = item->parentItem();
                }
            }

            IGraphicEditorItem* _item = dynamic_cast<IGraphicEditorItem*>(
                item);

            if (_item != 0)
            {
                ISimItem* simItem = dynamic_cast<ISimItem* >(_item);

                Q_ASSERT(simItem != 0); // can't happen

                if (!simItem->isInSimPath())
                {
                    tryToAddToSimPath(simItem, _item);
                }
                else
                {
                    tryToRemoveFromSimPath(simItem, _item);
                }
            }
        }
    }
    else
    {
        QGraphicsScene::mouseReleaseEvent(event);
    }
}

void SimGraphicData::tryToAddToSimPath(ISimItem* item,
    IGraphicEditorItem* _item)
{
    if (m_simPath.isEmpty() && _item->type() == SWP_SIM_START_POINT_ITEM)
    {
        m_simPath.append(_item->id());

        item->setInSimPath(true);
    }
    else if (!m_simPath.isEmpty())
    {
        const QUuid previousId = m_simPath.last();

        IGraphicEditorItem* previousItem = dynamic_cast<IGraphicEditorItem* >(
            m_idToInstanceMapping[previousId]);

        Q_ASSERT(previousItem != 0); // can't happen

        const QList<ConnectionData> connections = previousItem->connections();

        foreach (const ConnectionData& connection, connections)
        {
            if (connection.From == previousItem && connection.To == _item)
            {
                ISimItem* connector = dynamic_cast<ISimItem* >(
                    connection.Connector);

                Q_ASSERT(connector != 0); // connection has to be sim item

                connector->setInSimPath(true);

                m_simPath.append(connection.Connector->id());
                m_simPath.append(_item->id());

                item->setInSimPath(true);
                break;
            }
        }
    }
}

void SimGraphicData::tryToRemoveFromSimPath(ISimItem* item,
    IGraphicEditorItem* _item)
{
    if (m_simPath.contains(_item->id()) &&
        _item->type() != SWP_SIM_ACTIVITY_CONNECTOR_ITEM)
    {
        const int index = m_simPath.indexOf(_item->id());

        clearSimpath(index > 0 ? index - 1 : index);
    }
}

void SimGraphicData::checkSimPath()
{
    const bool valid = !m_simPath.isEmpty() ?
        (m_idToInstanceMapping[m_simPath.first()]->type() ==
            SWP_SIM_START_POINT_ITEM &&
         m_idToInstanceMapping[m_simPath.last()]->type() ==
            SWP_SIM_END_POINT_ITEM) : true;

    const QList<QUuid> oldSimPath = m_oldSimPath;

    if (!valid)
    {
        QMessageBox::StandardButton button = QMessageBox::question(
            MainWindow::getInstance(), tr("Invalid simulation path"),
            tr("The simulation path appears not to be valid "
            "(it doesn't start on Start point and it doesn't end in "
            "End point). Do you want to keep it anyway?"),
            QMessageBox::Yes | QMessageBox::No);

        if (button == QMessageBox::No)
        {
            clearSimpath();

            emit validChanged(true);
        }
        else
        {
            emit validChanged(false);
        }
    }

    m_oldSimPath = m_simPath;

    if (oldSimPath != m_simPath)
    {
        BuildSimPathCommand* command = new BuildSimPathCommand(this,
            oldSimPath, m_simPath, true);

        UndoManager::getInstance()->push(this, command);

        emit dataChanged();

        m_isNetDirty = true;
    }
}

void SimGraphicData::clearSimpath(int index)
{
    QList<QUuid> toRemoveItems;

    for (int i = index; i < m_simPath.size(); ++i)
    {
        const QUuid id = m_simPath[i];

        IGraphicEditorItem* toRemove = dynamic_cast<IGraphicEditorItem* >(
            m_idToInstanceMapping[id]);

        ISimItem* toRemoveSimItem = dynamic_cast<ISimItem* >(toRemove);

        Q_ASSERT(toRemoveSimItem != 0); // can't happen

        toRemoveSimItem->setInSimPath(false);

        toRemoveItems.append(id);
    }

    foreach(const QUuid& id, toRemoveItems)
    {
        m_simPath.removeAll(id);
    }

    if (index == 0)
    {
        m_oldSimPath.clear();
    }
}
